package it.koutifaris.travianmap.dal.sqlservices.dumpimporter;

import static it.koutifaris.testforclassutils.IsTestForAssert.assertIsTestFor;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import it.koutifaris.travianmap.TestRemoteSource;
import it.koutifaris.travianmap.dal.RevisionService.Revision;
import it.koutifaris.travianmap.dal.sqlservices.AbstractMapDbTest;
import it.koutifaris.travianmap.dal.sqlservices.MapSqlContract.Alliances;
import it.koutifaris.travianmap.dal.sqlservices.MapSqlContract.Players;
import it.koutifaris.travianmap.dal.sqlservices.MapSqlContract.Revisions;
import it.koutifaris.travianmap.dal.sqlservices.MapSqlContract.Villages;
import it.koutifaris.travianmap.mapsqlparser.MapSqlEntry;
import it.koutifaris.travianmap.mapsqlparser.MapSqlParser.RemoteSource;
import it.koutifaris.travianmap.mapsqlparser.RegexMapSqlParser;
import it.koutifaris.travianmap.utils.DayTruncUtils;
import it.koutifaris.travianmap.utils.cursor.ColumnNameCursor;
import it.koutifaris.travianmap.utils.cursor.ColumnNameCursorWrapper;

import java.io.IOException;
import java.util.Date;

import junit.framework.Assert;

public class TravianSqlDumpImporterTest extends AbstractMapDbTest {

	private TravianSqlDumpImporter importer;
	private Revision mockRevision;

	@Override
	protected void overridableSetUp() {
		mockRevision = mockRevision(new Date());
		importer = new TravianSqlDumpImporter(getDbHelper().getWritableDatabase());
		importer.init(mockRevision.getDay());
	}

	public void testPkgAndName() {
		assertIsTestFor(TravianSqlDumpImporter.class);
	}

	public void testRevisionTableImport() {
		callImporter();
		verifyRevisionTable();
	}

	public void testAllianceTableImport() {
		MapSqlEntry mockSqlEntry = callImporter();
		verifyAllianceTable(mockSqlEntry);
	}

	public void testPlayerTableImport() {
		MapSqlEntry mockSqlEntry = callImporter();
		verifyPlayerTable(mockSqlEntry);
	}

	public void testVillageTableImport() {
		MapSqlEntry mockSqlEntry = callImporter();
		verifyVillageTable(mockSqlEntry);
	}

	public void testFullInsert() throws IOException {
		RegexMapSqlParser sqlParser = new RegexMapSqlParser();
		RemoteSource remoteSource = new TestRemoteSource(new Date(), getClass().getResourceAsStream("ts6map.sql.gz"), true);

		sqlParser.parse(
				remoteSource,
				new TravianSqlDumpImporter(getDbHelper().getWritableDatabase()));
	}

	private MapSqlEntry callImporter() {
		MapSqlEntry mockSqlEntry = mockSqlEntry();

		importer.init(mockRevision.getDay());
		importer.onEntry(mockSqlEntry);
		importer.done();

		return mockSqlEntry;
	}

	private void verifyRevisionTable() {
		String[] columns = { Revisions.COLUMN_NAME_REVISION_ID, Revisions.COLUMN_NAME_DATE };

		ColumnNameCursor cursor = query(Revisions.TABLE_NAME, columns);
		cursor.moveToNext();

		verifyRevisionId(cursor.getLong(Revisions.COLUMN_NAME_REVISION_ID));

		long revisionDateMillis = cursor.getLong(Revisions.COLUMN_NAME_DATE);
		Assert.assertEquals(mockRevision.getDay(), new Date(revisionDateMillis));
	}

	private void verifyRevisionId(long revisionId) {
		Assert.assertEquals(mockRevision.getId(), revisionId);
	}

	private void verifyAllianceTable(MapSqlEntry mockSqlEntry) {
		String[] columns = { Alliances.COLUMN_NAME_REVISION_ID, Alliances.COLUMN_NAME_ALLIANCE_ID,
				Alliances.COLUMN_NAME_ALLIANCE_NAME };

		ColumnNameCursor cursor = query(Alliances.TABLE_NAME, columns);
		cursor.moveToNext();

		verifyRevisionId(cursor.getLong(Alliances.COLUMN_NAME_REVISION_ID));

		EntryVerifier entryVerifier = new EntryVerifier(mockSqlEntry);
		entryVerifier.verifyAlliance(cursor.getInt(Alliances.COLUMN_NAME_ALLIANCE_ID));
	}

	private void verifyPlayerTable(MapSqlEntry mockSqlEntry) {
		String[] columns = {
				Players.COLUMN_NAME_REVISION_ID,
				Players.COLUMN_NAME_PLAYER_ID,
				Players.COLUMN_NAME_PLAYER_NAME,
				Players.COLUMN_NAME_ALLIANCE_ID,
				Players.COLUMN_NAME_TRIBE };

		ColumnNameCursor cursor = query(Players.TABLE_NAME, columns);
		cursor.moveToNext();

		verifyRevisionId(cursor.getLong(Players.COLUMN_NAME_REVISION_ID));

		EntryVerifier entryVerifier = new EntryVerifier(mockSqlEntry);
		entryVerifier.verifyPlayerId(cursor.getInt(Players.COLUMN_NAME_PLAYER_ID));
		entryVerifier.verifyPlayerName(cursor.getString(Players.COLUMN_NAME_PLAYER_NAME));
		entryVerifier.verifyAlliance(cursor.getInt(Players.COLUMN_NAME_ALLIANCE_ID));
		entryVerifier.verifyTribe(cursor.getInt(Players.COLUMN_NAME_TRIBE));
	}

	private void verifyVillageTable(MapSqlEntry mockSqlEntry) {
		String[] columns = {
				Villages.COLUMN_NAME_REVISION_ID,
				Villages.COLUMN_NAME_VILLAGE_ID,
				Villages.COLUMN_NAME_VILLAGE_NAME,
				Villages.COLUMN_NAME_POPULATION,
				Villages.COLUMN_NAME_X,
				Villages.COLUMN_NAME_Y,
				Villages.COLUMN_NAME_OWNER_ID };

		ColumnNameCursor cursor = query(Villages.TABLE_NAME, columns);
		cursor.moveToNext();

		verifyRevisionId(cursor.getLong(Villages.COLUMN_NAME_REVISION_ID));

		EntryVerifier entryVerifier = new EntryVerifier(mockSqlEntry);
		entryVerifier.verifyVillageId(cursor.getInt(Villages.COLUMN_NAME_VILLAGE_ID));
		entryVerifier.verifyVillageName(cursor.getString(Villages.COLUMN_NAME_VILLAGE_NAME));
		entryVerifier.verifyVillagePopulation(cursor.getInt(Villages.COLUMN_NAME_POPULATION));
		entryVerifier.verifyVillageX(cursor.getInt(Villages.COLUMN_NAME_X));
		entryVerifier.verifyVillageY(cursor.getInt(Villages.COLUMN_NAME_Y));
		entryVerifier.verifyPlayerId(cursor.getInt(Villages.COLUMN_NAME_OWNER_ID));
	}

	private ColumnNameCursorWrapper query(String tableName, String[] columns) {
		return new ColumnNameCursorWrapper(getDbHelper().getReadableDatabase().query(tableName, columns, null, null, null,
				null, null));
	}

	private MapSqlEntry mockSqlEntry() {
		final MapSqlEntry mockedEntry = mock(MapSqlEntry.class);

		when(mockedEntry.getSquareId()).thenReturn(754);
		when(mockedEntry.getX()).thenReturn(353);
		when(mockedEntry.getY()).thenReturn(400);
		when(mockedEntry.getTribeId()).thenReturn(2);
		when(mockedEntry.getVillageId()).thenReturn(120516);
		when(mockedEntry.getVillageName()).thenReturn("-R2-Susina");
		when(mockedEntry.getPlayerId()).thenReturn(10852);
		when(mockedEntry.getPlayerName()).thenReturn("nathan");
		when(mockedEntry.getAllianceId()).thenReturn(1347);
		when(mockedEntry.getAllianceName()).thenReturn("SOLI");
		when(mockedEntry.getVillagePopulation()).thenReturn(25);

		return mockedEntry;
	}

	private Revision mockRevision(Date date) {
		Revision revision = mock(Revision.class);
		Date day = DayTruncUtils.truncDateToDay(date);

		when(revision.getId()).thenReturn(day.getTime());
		when(revision.getDay()).thenReturn(day);

		return revision;
	}
}
